/**
 * 	 DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *   @author Colin Redmond
 *   Copyright (C) 2011 Colin Redmond
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */

package com.cred.industries.platform.resources;

import java.net.URI;
import java.util.ArrayList;
import java.util.List;

import javax.annotation.security.PermitAll;
import javax.annotation.security.RolesAllowed;
import javax.ws.rs.Consumes;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.cred.industries.core.exceptions.ApplicationException;
import com.cred.industries.core.util.ExceptionResponse;
import com.cred.industries.platform.business.facade.CustomerFacade;
import com.cred.industries.platform.business.facade.ForgotPasswordFacade;
import com.cred.industries.platform.business.objects.CustomerBO;
import com.cred.industries.platform.converter.CustomerConverter;
import com.cred.industries.platform.defines.Role;
import com.cred.industries.platform.session.SessionData;
import com.cred.industries.platform.transferObj.CustomerTrans;
import com.cred.industries.platform.transferObj.CustomerTransList;
import com.cred.industries.platform.transferObj.DetailedCustomerTrans;
import com.cred.industries.platform.transferObj.LinkTrans;

/**
 * Jersey registers the path so a client can call directly to this class
 */
@Path("/customer")
public class CustomerRes {
	private static final Logger logger = LoggerFactory.getLogger(CustomerRes.class);
	
	@Context UriInfo mUri;

	/**
	 * returns logged in user
	 * @return logged in user
	 */
	@GET
	@RolesAllowed({Role.ADMIN_STRING, Role.CS_STRING, Role.USER_STRING})
	public String getLoggedinUser() {
		
		CustomerBO cust = SessionData.getSessionCustomer();
		return cust.getPersonaName(); 
	}
	
	/**
	 * called by clients to get a customer by ID
	 * @param custId customer ID to get
	 * @return customer Trans that matches the Customer ID
	 * @throws WebApplicationException, used by jersey to build a responce for the clients
	 */
	@GET
	@Path("/{id: \\d+}")
	@Consumes(MediaType.TEXT_PLAIN)
	@Produces(MediaType.APPLICATION_XML)
	@RolesAllowed({Role.ADMIN_STRING, Role.CS_STRING, Role.USER_STRING})
	public CustomerTrans getCustomerById(@PathParam("id") int custId) {
		
		CustomerTrans custTrans = null;

		try {
			//use the customer facade to find the customer
			CustomerFacade custFacade = new CustomerFacade();
			custTrans = CustomerConverter.convert(custFacade.getCustomerById(custId));
			
			//if we didn't find the customer return that the resource isn't found
			//this is to comply with rest best practices
			if(custTrans == null) {
				logger.error("unable to get custTrans " + custId);
				throw new WebApplicationException(ExceptionResponse.build(Status.NOT_FOUND));
			} 
			
			//return the found customer
			return custTrans;
		} catch (ApplicationException | WebApplicationException e) {
			logger.error("Exception " + e.toString() + " " + e.getMessage());
			e.printStackTrace();
			throw e;
		} catch (Throwable e) {
			logger.error("Exception " + e.toString() + " " + e.getMessage());
			e.printStackTrace();
			throw new WebApplicationException(ExceptionResponse.build(e));
		} 
	}
	
	/**
	 * Gets a group of customers that match a persona name.  
	 * @param custName persona name to match. 
	 * @param setStart where to start searching in the DB, ie the 20th customer
	 * @param setSize number of customers to get
	 * @return the set of customers found
	 * @throws WebApplicationException, used by jersey to build a responce for the clients
	 */
	@GET
	@Path("/group/{id}")
	@Consumes(MediaType.TEXT_PLAIN)
	@Produces(MediaType.APPLICATION_XML)
	@RolesAllowed({Role.ADMIN_STRING, Role.CS_STRING, Role.USER_STRING})
	public CustomerTransList getCustomersByName(@PathParam("id") String custName, 
				@DefaultValue("0") @QueryParam("start") int setStart, 
				@DefaultValue("10") @QueryParam("size") int setSize) {
		
		List<CustomerTrans> custTrans = new ArrayList<>();

		try {
			if(custName.length() < 3) {
				throw new WebApplicationException(ExceptionResponse.build(Status.BAD_REQUEST, "string must be at least 3 characters long"));
			}
			
			//use the customer facade to find the customer
			CustomerFacade custFacade = new CustomerFacade();
			//we get the set size plus one so we can tell if there is a next result set
			custTrans = CustomerConverter.convert(custFacade.getCustomersByName(custName, setStart, setSize + 1));
		
			//if we didn't find the customer return that the resource isn't found
			//this is to comply with rest best practices
			if(custTrans == null || custTrans.size() == 0) {
				logger.error("unable to get find any customers in getCustomersByName " + custName);
				throw new WebApplicationException(ExceptionResponse.build(Status.NOT_FOUND));
			} 
			
			//when returning the links to the next/previous sets we need to figure out where we are
			//since we tried to grab one more than the asked for size we can tell if there are more
			//results. If there are more results we will add a link to them and remove the extra  
			//customer found
			boolean isNextSet = false;
			if(custTrans.size() > setSize) {
				isNextSet = true;
				custTrans.remove(setSize);
			}
			
			URI next = null;
			URI prev = null;
						
			//the next link is the start plus the number of cust found
			UriBuilder linkBuilder = mUri.getAbsolutePathBuilder();
			if(isNextSet) {
				linkBuilder.queryParam("start", setStart + custTrans.size());
				linkBuilder.queryParam("size", setSize);
				next = linkBuilder.build();
			}
			
			//the previous link is the min of start minus the result set size or 0. If 0 we dont send a link back
			int previousStart =  Math.max(setStart - setSize, 0);
			if(setStart != 0) {
				linkBuilder.queryParam("start", previousStart);
				linkBuilder.queryParam("size", setSize);
				prev = linkBuilder.build();
			}
			
			//add the found customers
			CustomerTransList result = new CustomerTransList(custTrans);
			if(next != null) {
				result.setNext(new LinkTrans("next", next.toString()));
			} else {
				result.setNext(new LinkTrans("next", ""));
			}
			
			if(prev != null) {
				result.setPrevious(new LinkTrans("prev", prev.toString()));
			} else {
				result.setPrevious(new LinkTrans("prev", ""));
			}
			
			return result;
		} catch (ApplicationException | WebApplicationException e) {
			logger.error("Exception " + e.toString() + " " + e.getMessage());
			e.printStackTrace();
			throw e;
		} catch (Throwable e) {
			logger.error("Exception " + e.toString() + " " + e.getMessage());
			e.printStackTrace();
			throw new WebApplicationException(ExceptionResponse.build(e));
		} 
	}

	/**
	 * posting to this resource will create a new resource under /consumer/{id}
	 * We permit anyone to create a customer, since if you need to log in to log in 
	 * we wouldn't get far
	 * @param custTrans customer to create
	 * @return the customer trans of the new customer created
	 * @throws WebApplicationException, used by jersey to build a responce for the clients
	 */
	@POST
	@Path("/create")
	@Consumes(MediaType.APPLICATION_XML)
	@Produces(MediaType.APPLICATION_XML)
	@PermitAll
	public CustomerTrans createCustomer(DetailedCustomerTrans custTrans) {
		
		try {
			//use the customer facade to create a new customer with the CreateCustomerTrans
			CustomerFacade custFacade = new CustomerFacade();
			CustomerBO custCreated = custFacade.createCustomer(CustomerConverter.convert(custTrans));
			return CustomerConverter.convert(custCreated);
		} catch (ApplicationException | WebApplicationException e) {
			logger.error("Exception " + e.toString() + " " + e.getMessage());
			e.printStackTrace();
			throw e;
		} catch (Throwable e) {
			logger.error("Exception " + e.toString() + " " + e.getMessage());
			e.printStackTrace();
			throw new WebApplicationException(ExceptionResponse.build(e));
		} 
	}
	
	/**
	 * Allows us to modify existing customers
	 * @param custTrans customer to modify
	 * @return the customer that was modified
	 * @throws WebApplicationException, used by jersey to build a responce for the clients
	 */
	@PUT
	@Consumes(MediaType.APPLICATION_XML)
	@RolesAllowed({Role.ADMIN_STRING, Role.CS_STRING, Role.USER_STRING})
	public DetailedCustomerTrans updateCustomer(DetailedCustomerTrans custTrans) {
		
		try {
			//use the customer facade to update the customer in the DetailedCustomerTrans
			CustomerFacade custFacade = new CustomerFacade();
		
			CustomerBO custCreated = custFacade.updateCustomer(CustomerConverter.convert(custTrans));
			DetailedCustomerTrans updatedCust = CustomerConverter.convertDetailed(custCreated);
			
			//make a copy of the DetailedCustomerTrans without the password
			DetailedCustomerTrans returnCust = new DetailedCustomerTrans(updatedCust.getCustomerId()
													, updatedCust.getPersonaName()
													, ""
													, updatedCust.getFirstName()
													, updatedCust.getLastName()
													, updatedCust.getEmailAddress());
			
			return returnCust;
			
		} catch (ApplicationException | WebApplicationException e) {
			logger.error("Exception " + e.toString() + " " + e.getMessage());
			e.printStackTrace();
			throw e;
		} catch (Throwable e) {
			logger.error("Exception " + e.toString() + " " + e.getMessage());
			e.printStackTrace();
			throw new WebApplicationException(ExceptionResponse.build(e));
		} 
	}
	
	@POST
	@Path("/password")
	@Consumes(MediaType.TEXT_PLAIN)
	@Produces(MediaType.TEXT_PLAIN)
	@PermitAll
	public Response resetPassword(@QueryParam("emailAddress") String emailAddress) {
		
		try {
			
			//	use the ForgotPasswordFacade to send an email to reset the password
			ForgotPasswordFacade forgotPassF = new ForgotPasswordFacade();
			forgotPassF.resetPassword(emailAddress, true);
			
			return Response.ok().build();
		} catch (ApplicationException | WebApplicationException e) {
			logger.error("Exception " + e.toString() + " " + e.getMessage());
			e.printStackTrace();
			throw e;
		} catch (Throwable e) {
			logger.error("Exception " + e.toString() + " " + e.getMessage());
			e.printStackTrace();
			throw new WebApplicationException(ExceptionResponse.build(e));
		} 
	}
}
